package com.bio.jsf;

import com.bio.jpa.entities.File;
import com.bio.jpa.entities.FileData;
import com.bio.jsf.util.JsfUtil;
import com.bio.jpa.session.FileFacade;
import com.bio.jsf.util.ProgressBean;
import com.bio.tests.GenBankParser;

import java.io.*;
import java.net.URLConnection;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ejb.EJB;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.FacesConverter;
import javax.faces.model.SelectItem;
import javax.inject.Inject;
import org.apache.commons.io.FileUtils;
import org.primefaces.model.DefaultStreamedContent;
import org.primefaces.model.StreamedContent;
import org.primefaces.model.UploadedFile;

@ManagedBean(name = "fileController")
@SessionScoped
public class FileController implements Serializable {

    private File current;
    private List<File> items = new ArrayList<File>();
    private String uploadDirectory;
    @EJB
    private FileFacade ejbFacade;
    private static final Logger logger = Logger.getLogger(FileController.class.getName());

    public java.io.File retrieveFile(String path) {
        return new java.io.File(path);
    }

    public StreamedContent convert(java.io.File file) {
        FileInputStream input = null;
        try {
            input = new FileInputStream(file);
        } catch (FileNotFoundException ex) {
            Logger.getLogger(ProjectController.class.getName()).log(Level.SEVERE, null, ex);
        }
        return new DefaultStreamedContent(input, URLConnection.guessContentTypeFromName(file.getName()), file.getName());
    }

    public String getUploadDirectory() {
        FacesContext ctx = FacesContext.getCurrentInstance();
        uploadDirectory =
                ctx.getExternalContext().getInitParameter("uploadDirectory");
        uploadDirectory = FacesContext.getCurrentInstance().getExternalContext().getRealPath(uploadDirectory);

        return uploadDirectory + "//";
    }

    public void setUploadDirectory(String uploadDirectory) {
        this.uploadDirectory = uploadDirectory;
    }

    public FileController() {
    }

    public File getCurrent() {
        if (current == null) {
            current = new File();
        }
        return current;
    }

    public java.io.File unChunckFile(File file) {
        java.io.File result = new java.io.File(getUploadDirectory() + file.getFileName());
        if (result.exists()) {
            logger.log(Level.INFO, "File {0} already exist ", new Object[]{file.getTitle()});
            return result;
        }
        try {
            logger.log(Level.INFO, "File {0} is being composed ", new Object[]{file.getTitle()});
            FileOutputStream fos = new FileOutputStream(result);
            byte[] bytes;
            Collections.sort(file.getFileDataList());
            int size = file.getFileDataList().size();
            // Continue writing bytes until there are no more
            for (int i = 0; i < size; i++) {
                FileData fd = file.getFileDataList().get(i);
                bytes = fd.getFileData();
                logger.log(Level.CONFIG, String.valueOf(bytes.length));
                fos.write(bytes, 0, bytes.length);
            }
            System.out.println("Saved File size " + result.length());
            // Close the file
            fos.close();
        } catch (IOException ex) {
            Logger.getLogger(FileController.class.getName()).log(Level.SEVERE, null, ex);
        }
        return result;
    }

    public static byte[] getBytesFromFile(java.io.File file) throws IOException, Exception {
        InputStream is = new FileInputStream(file);

        // Get the size of the file
        long length = file.length();

        // You cannot create an array using a long type.
        // It needs to be an int type.
        // Before converting to an int type, check
        // to ensure that file is not larger than Integer.MAX_VALUE.
        if (length > Integer.MAX_VALUE) {
            // File is too large
            throw new Exception("File is too large");
        }

        // Create the byte array to hold the data
        byte[] bytes = new byte[(int) length];

        // Read in the bytes
        int offset = 0;
        int numRead = 0;
        while (offset < bytes.length
                && (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) {
            offset += numRead;
        }

        // Ensure all the bytes have been read in
        if (offset < bytes.length) {
            throw new IOException("Could not completely read file " + file.getName());
        }

        // Close the input stream and return bytes
        is.close();
        return bytes;
    }

    public static byte[] getBytesFromFile(UploadedFile file) {
        InputStream is = null;
        long length = file.getSize();
        // Create the byte array to hold the data
        byte[] bytes = new byte[(int) length];
        try {
            is = file.getInputstream();
            // Get the size of the file

            // You cannot create an array using a long type.
            // It needs to be an int type.
            // Before converting to an int type, check
            // to ensure that file is not larger than Integer.MAX_VALUE.
            if (length > Integer.MAX_VALUE) {
                throw new IOException("File is too large " + file.getFileName());
            }

            // Read in the bytes
            int offset = 0;
            int numRead = 0;
            while (offset < bytes.length
                    && (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) {
                offset += numRead;
            }
            // Ensure all the bytes have been read in
            if (offset < bytes.length) {
                throw new IOException("Could not completely read file " + file.getFileName());
            }
            // Close the input stream and return bytes
            is.close();

        } catch (IOException ex) {
            Logger.getLogger(FileController.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            try {
                is.close();
            } catch (IOException ex) {
                Logger.getLogger(FileController.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

        return bytes;
    }

    public boolean headOfFile(UploadedFile f, String pattern, int lines) {
        String lineData = "";
        try {
            Scanner scan = new Scanner(f.getInputstream());
            int curr = 0;
            while (curr < lines) {
                lineData = scan.nextLine();
                if (lineData.contains(pattern)) {
                    scan.close();
                    return true;
                }
                curr++;
            }
            scan.close();
        }//try
        catch (IOException ex) {
            System.err.println(ex.getMessage());
        }//catch
        return false;
    }
     public boolean headOfFile(java.io.File f, String pattern, int lines) {
        String lineData = "";
        try {
            Scanner scan = new Scanner(f);
            int curr = 0;
            while (curr < lines) {
                lineData = scan.nextLine();
                if (lineData.contains(pattern)) {
                    scan.close();
                    return true;
                }
                curr++;
            }
            scan.close();
        }//try
        catch (IOException ex) {
            System.err.println(ex.getMessage());
        }//catch
        return false;
    }

    protected List<FileData> chunkFile(UploadedFile ufile) {

        FileDataController controller = (FileDataController) FacesContext.getCurrentInstance().getApplication().getELResolver().
                getValue(FacesContext.getCurrentInstance().getELContext(), null, "fileDataController");
        List<FileData> list = new ArrayList<FileData>();
        byte toStore[] = getBytesFromFile(ufile);
        int size = toStore.length;

        int block_size = 16 * 1024;
        double pieces = (size / block_size) + 1;
        int remaining = size;
        System.out.println("File Size " + size + " Pieces " + pieces);
        int offset = 0;
        for (int i = 0; i < pieces; i++) {
            FileData fd = new FileData();
            fd.setSeqId(i);
            fd.setFileIdFile(current);
            if (remaining < block_size) {
                fd.setFileData(Arrays.copyOfRange(toStore, offset, offset + remaining));
            } else {
                fd.setFileData(Arrays.copyOfRange(toStore, offset, offset + block_size));
            }

            remaining = remaining - block_size;
            // System.out.println("remaining " + remaining);
            offset = block_size + offset;
            //System.out.println("Start " + offset);
            //controller.setCurrent(fd);
            //controller.create();

            list.add(fd);
        }
        return list;

    }

    static class FileProcessor implements Runnable {

        private java.io.File file;
        private UploadedFile ufile;
        private File dbfile;
        private FileController fc;

        public File getDbfile() {
            return dbfile;
        }

        public FileProcessor(java.io.File file, UploadedFile ufile, File dbfile, FileController fc) {
            this.file = file;
            this.ufile = ufile;
            this.dbfile = dbfile;
            this.fc = fc;
        }

        public void run() {

            try {
                FileOutputStream fileOutputStream = new FileOutputStream(file);

                if (!file.exists()) {
                    file.createNewFile();
                }
                byte[] buffer = new byte[16 * 1024];

                int bulk;

                InputStream inputStream = ufile.getInputstream();
                while (true) {
                    bulk = inputStream.read(buffer);
                    if (bulk < 0) {
                        break;
                    }
                    fileOutputStream.write(buffer, 0, bulk);
                    fileOutputStream.flush();
                }
                fileOutputStream.close();
                inputStream.close();

                if (file.exists()) {

                    double bytes = file.length();
                    double kilobytes = (bytes / 1024);
                    double megabytes = (kilobytes / 1024);
                    double gigabytes = (megabytes / 1024);
                    double terabytes = (gigabytes / 1024);
                    double petabytes = (terabytes / 1024);
                    double exabytes = (petabytes / 1024);
                    double zettabytes = (exabytes / 1024);
                    double yottabytes = (zettabytes / 1024);
                }

            } catch (IOException e) {
                e.printStackTrace();
            }
            logger.info("File [" + dbfile.getTitle() + " was saved on server");

        }

        public void store() {
            fc.setCurrent(dbfile);
            fc.create();
        }
    }

    public static java.io.File saveToFile(StringBuilder sb, String file, boolean append) {
        long start = System.currentTimeMillis();
        java.io.File f = new java.io.File(file);

        try {
            FileUtils.writeStringToFile(f, sb.toString(), append);
            System.out.println("File Saved in " + f.getAbsolutePath());
        } catch (IOException ex) {
            Logger.getLogger(FileController.class.getName()).log(Level.SEVERE, null, ex);
        }
        long end = System.currentTimeMillis();
        System.out.println((end - start) / 1000f + " seconds");

        return f;
    }

    public static java.io.File saveToFile(UploadedFile u) {
        long start = System.currentTimeMillis();
        java.io.File f = new java.io.File(u.getFileName());
        FileUtils.deleteQuietly(f);
        try {
            FileUtils.writeByteArrayToFile(f, getBytesFromFile(u), false);
        } catch (IOException ex) {
            Logger.getLogger(FileController.class.getName()).log(Level.SEVERE, null, ex);
        }
        long end = System.currentTimeMillis();
        System.out.println((end - start) / 1000f + " seconds");
        return f;
    }

    private static java.io.File writeBuffered(List<String> records, int bufSize, java.io.File f) throws IOException {
        FileWriter writer = new FileWriter(f);
        BufferedWriter bufferedWriter = new BufferedWriter(writer, bufSize);
        System.out.print("Writing buffered (buffer size: " + bufSize + ")... ");
        write(records, bufferedWriter);
        return f;
    }

    private static void write(List<String> records, Writer writer) throws IOException {
        long start = System.currentTimeMillis();
        for (String record : records) {
            writer.write(record);
        }
        writer.flush();
        writer.close();
        long end = System.currentTimeMillis();
        System.out.println((end - start) / 1000f + " seconds");
    }

    private void storeByteArrayToFile(byte[] bytesToSave, String path) throws IOException {
        FileOutputStream fOut = new FileOutputStream(path);

        try {
            fOut.write(bytesToSave);
        } catch (Exception ex) {
            logger.log(Level.SEVERE, ex.getMessage());
        } finally {
            fOut.close();
        }
    }

    public void setCurrent(File current) {
        this.current = current;
    }

    public List<File> getItems() {
        if (items == null) {
            items = getFacade().findAll();
        }
        return items;
    }

    public List<File> getFilesByProject(int id) {
        return getFacade().findByprojectPrjId(id);
    }

    public File getFileById(int id) {
        return getFacade().find(id);
    }

    public void setItems(List<File> items) {
        this.items = items;
    }

    private FileFacade getFacade() {
        return ejbFacade;
    }

    public String prepareList() {
        return "List";
    }

    public String prepareView() {
        return "View";
    }

    public String prepareCreate() {
        current = new File();
        return "Create";
    }

    public void create() {
        try {
            getFacade().create(current);
            JsfUtil.addSuccessMessage("File was saved");
        } catch (Exception e) {
            e.printStackTrace();
            JsfUtil.addErrorMessage(e, ResourceBundle.getBundle("/Bundle").getString("PersistenceErrorOccured"));
        }
    }

    public String prepareEdit() {
        return "Edit";
    }

    public String update() {
        try {
            getFacade().edit(current);
            JsfUtil.addSuccessMessage(ResourceBundle.getBundle("/Bundle").getString("FileUpdated"));
            return "View";
        } catch (Exception e) {
            JsfUtil.addErrorMessage(e, ResourceBundle.getBundle("/Bundle").getString("PersistenceErrorOccured"));
            return null;
        }
    }

    public String destroy() {
        performDestroy();
        return "List";
    }

    public String destroyAndView() {
        performDestroy();
        return "List";
    }

    private void performDestroy() {
        try {
            getFacade().remove(current);
            current = null;
            JsfUtil.addSuccessMessage("File Removed");
        } catch (Exception e) {
            JsfUtil.addErrorMessage(e, ResourceBundle.getBundle("/Bundle").getString("PersistenceErrorOccured"));
        }
    }

    public SelectItem[] getItemsAvailableSelectMany() {
        return JsfUtil.getSelectItems(ejbFacade.findAll(), false);
    }

    public SelectItem[] getItemsAvailableSelectOne() {
        return JsfUtil.getSelectItems(ejbFacade.findAll(), true);
    }

    @FacesConverter(forClass = File.class)
    public static class FileControllerConverter implements Converter {

        public Object getAsObject(FacesContext facesContext, UIComponent component, String value) {
            if (value == null || value.length() == 0) {
                return null;
            }
            FileController controller = (FileController) facesContext.getApplication().getELResolver().
                    getValue(facesContext.getELContext(), null, "FileController");
            return controller.ejbFacade.find(getKey(value));
        }

        java.lang.Long getKey(String value) {
            java.lang.Long key;
            key = Long.valueOf(value);
            return key;
        }

        String getStringKey(Integer value) {
            StringBuffer sb = new StringBuffer();
            sb.append(value);
            return sb.toString();
        }

        public String getAsString(FacesContext facesContext, UIComponent component, Object object) {
            if (object == null) {
                return null;
            }
            if (object instanceof File) {
                File o = (File) object;
                return getStringKey(o.getIdFile());
            } else {
                throw new IllegalArgumentException("object " + object + " is of type " + object.getClass().getName() + "; expected type: " + FileController.class.getName());
            }
        }
    }
}
